package spring.cloud.tasks.executor.listener.election;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import spring.cloud.tasks.executor.common.Service;
import spring.cloud.tasks.executor.node.TaskNodeStorage;
import spring.cloud.tasks.executor.task.TaskExecutor;
import spring.cloud.tasks.executor.utils.log.LogUtils;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 选举主节点的服务.
 */
@Slf4j
@Setter
@Getter
public class LeaderElectionService extends Service {
    public AtomicBoolean isShutdown = new AtomicBoolean(false);

    public LeaderElectionService(final TaskExecutor jobScheduler) {
        super(jobScheduler);
    }

    @Override
    public void stop() {
        synchronized (isShutdown) {
            if (isShutdown.compareAndSet(false, true)) {
                try { // Release my leader position
                    TaskNodeStorage taskNodeStorage = this.getTaskNodeStorage();
                    if (taskNodeStorage.isConnected() && executorId.equals(taskNodeStorage.getTaskNodeData(ElectionNode.LEADER_HOST))) {
                        taskNodeStorage.removeTaskNodeIfExisted(ElectionNode.LEADER_HOST);
                        LogUtils.info(log, taskId, "{} that was {}'s leader, released itself", executorId, taskId);
                    }
                } catch (Throwable t) {
                    LogUtils.error(log, taskId, t.getMessage(), t);
                }
            }
        }
    }


    /**
     * 判断当前节点是否是主节点，如果没有主节点，则选举，直到有主节点
     *
     * @return 当前节点是否是主节点
     */
    public Boolean isLeader() {
        while (!isShutdown.get() && !hasLeader()) {
            LogUtils.info(log, taskId, "No leader, try to election");
            leaderElection();
        }
        return executorId.equals(this.getTaskNodeStorage().getTaskNodeData(ElectionNode.LEADER_HOST));
    }

    /**
     * 判断是否已经有主节点
     *
     * @return 是否已经有主节点
     */
    public boolean hasLeader() {
        return this.getTaskNodeStorage().isTaskNodeExisted(ElectionNode.LEADER_HOST);
    }

    /**
     * 选举主节点.
     */
    public void leaderElection() {
        this.getTaskNodeStorage().executeInLeader(ElectionNode.LATCH, new LeaderElectionLeaderExecuteCallback(this));
    }


}
